package com.yc.user.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yc.base.BaseServiceImpl;
import com.yc.config.RsaProperties;
import com.yc.dept.entity.Dept;
import com.yc.dept.service.IDeptService;
import com.yc.domain.LoginUserDto;
import com.yc.enums.StatusEnum;
import com.yc.exception.ApiException;
import com.yc.exception.Asserts;
import com.yc.helper.LoginHelper;
import com.yc.menu.entity.Menu;
import com.yc.menu.service.IMenuService;
import com.yc.post.entity.Post;
import com.yc.post.service.IPostService;
import com.yc.role.entity.Role;
import com.yc.role.service.IRoleService;
import com.yc.user.entity.User;
import com.yc.user.entity.UserRoleRelation;
import com.yc.user.mapper.UserMapper;
import com.yc.user.service.IUserRoleRelationService;
import com.yc.user.service.IUserService;
import com.yc.user.vo.UserVo;
import com.yc.util.EntityUtils;
import com.yc.util.RsaUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 后台用户表 服务实现类
 * </p>
 *
 * @author bao
 * @since 2021-12-08
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements IUserService {

    private final IMenuService menuService;
    private final IDeptService deptService;
    private final IRoleService roleService;
    private final IPostService postService;
    private final IUserRoleRelationService userRoleRelationService;

    @Override
    public UserVo create(User user, List<Long> roleIds) {
        User dbUser = queryByUsername(user.getUsername());
        if (ObjectUtil.isNotNull(dbUser)) {
            Asserts.fail("用户名已存在");
        }

        //将密码进行加密操作
        String encodePassword = BCrypt.hashpw(user.getPassword());
        user.setPassword(encodePassword);
        save(user);
        UserVo userVo = new UserVo(user);
        Dept dept = deptService.queryById(userVo.getDeptId());
        userVo.setDept(dept);
        Post post = postService.queryById(userVo.getPostId());
        userVo.setPost(post);
        createUserRole(userVo, roleIds);
        return userVo;
    }

    /**
     * 新增用户角色信息
     */
    public void createUserRole(UserVo userVo, List<Long> roleIds) {
        if (CollectionUtils.isEmpty(roleIds)) {
            return;
        }
        roleIds = roleIds.stream()
                .distinct()
                .collect(Collectors.toList());
        List<Role> roleList = roleService.queryByIds(roleIds);
        if (roleList.size() == roleIds.size()) {
            List<UserRoleRelation> list = new ArrayList<>();
            for (Long id : roleIds) {
                UserRoleRelation userRoleRelation = new UserRoleRelation();
                userRoleRelation.setUserId(userVo.getId());
                userRoleRelation.setRoleId(id);
                list.add(userRoleRelation);
            }
            if (!CollectionUtils.isEmpty(list)) {
                boolean isOk = userRoleRelationService.saveBatch(list);
                if (isOk) {
                    userVo.setRoleList(roleList);
                } else {
                    Asserts.fail("关联角色失败");
                }
            }
        } else {
            Asserts.fail("角色数据有误");
        }
    }

    @Override
    public boolean createBatch(List<User> userList) {
        return saveBatch(userList);
    }

    @Override
    public boolean delete(List<Long> ids) {
        return removeByIds(ids);
    }

    @Override
    public UserVo update(User user, List<Long> roleIds) {
        if (!CollectionUtils.isEmpty(roleIds)) {
            userRoleRelationService.deleteByUid(user.getId());
        }
        updateById(user);
        UserVo userVo = new UserVo(user);
        Dept dept = deptService.queryById(userVo.getDeptId());
        userVo.setDept(dept);
        Post post = postService.queryById(userVo.getPostId());
        userVo.setPost(post);
        if (!CollectionUtils.isEmpty(roleIds)) {
            createUserRole(userVo, roleIds);
        }
        return userVo;
    }

    @Override
    public UserVo queryById(Long id) {
        User user = getById(id);
        UserVo userVo = Optional.ofNullable(user).map(UserVo::new).orElse(null);
        assert userVo != null;
        List<UserRoleRelation> userRoleRelationList = userRoleRelationService.list(Wrappers.lambdaQuery(UserRoleRelation.class)
                .eq(UserRoleRelation::getUserId, userVo.getId()));
        //角色id
        Set<Long> roleIds = userRoleRelationList.stream().map(UserRoleRelation::getRoleId).collect(Collectors.toSet());
        List<Role> roleList = roleService.list(Wrappers.lambdaQuery(Role.class).in(Role::getId, roleIds));
        List<Dept> deptList = deptService.list(Wrappers.emptyWrapper());
        List<Post> postList = postService.list(Wrappers.emptyWrapper());

        userVo.setRoleList(roleList);
        //部门
        Dept dept = deptList.stream().filter(e -> e.getId().equals(userVo.getDeptId())).findFirst().orElse(null);
        userVo.setDept(dept);
        //岗位
        Post post = postList.stream().filter(e -> e.getId().equals(userVo.getPostId())).findFirst().orElse(null);
        userVo.setPost(post);
        return userVo;
    }

    @Override
    public List<User> selectList() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.lambda().orderByDesc(User::getId);
        return list(wrapper);
    }

    @Override
    public IPage<UserVo> selectPage(IPage<User> page, String username, List<Long> roleIdList, Long postId, StatusEnum statusEnum) {
        LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery(User.class)
                .like(StringUtils.isNotEmpty(username), User::getUsername, username)
                .eq(ObjectUtil.isNotNull(statusEnum), User::getStatus, ObjectUtil.isNotNull(statusEnum) ? statusEnum.getValue() : null)
                .orderByDesc(User::getId);
        IPage<UserVo> userVoPage = EntityUtils.toPage(page(page, queryWrapper), UserVo::new);
        //用户id
        Set<Long> userIds = userVoPage.getRecords().stream().map(User::getId).collect(Collectors.toSet());
        List<UserRoleRelation> userRoleRelationList = userRoleRelationService.list(Wrappers.lambdaQuery(UserRoleRelation.class)
                .in(UserRoleRelation::getUserId, userIds));
        //角色id
        Set<Long> roleIds = userRoleRelationList.stream().map(UserRoleRelation::getRoleId).collect(Collectors.toSet());
        if (CollectionUtil.isNotEmpty(userIds) && CollectionUtil.isNotEmpty(roleIds)) {
            Map<Long, Long> map = userRoleRelationList.stream().collect(Collectors.toMap(UserRoleRelation::getUserId, UserRoleRelation::getRoleId));
            List<Role> roleList = roleService.list(Wrappers.lambdaQuery(Role.class).in(Role::getId, roleIds));
            List<Dept> deptList = deptService.list(Wrappers.emptyWrapper());
            List<Post> postList = postService.list(Wrappers.emptyWrapper());
            for (UserVo userVo : userVoPage.getRecords()) {
                List<Role> roles = roleList.stream().filter(e -> e.getId().equals(map.get(userVo.getId()))).toList();
                userVo.setRoleList(roles);
                //部门
                Dept dept = deptList.stream().filter(e -> e.getId().equals(userVo.getDeptId())).findFirst().orElse(null);
                userVo.setDept(dept);
                //岗位
                Post post = postList.stream().filter(e -> e.getId().equals(userVo.getPostId())).findFirst().orElse(null);
                userVo.setPost(post);
            }
        }

        return userVoPage;
    }

    @Override
    public User queryByUsername(String username) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(User::getUsername, username);
        return getOne(wrapper);
    }

    @Override
    public LoginUserDto loadUserByUsername(String username) {
        User user = queryByUsername(username);
        if (ObjectUtil.isNotNull(user)) {
            List<Menu> menuList = menuService.selectMenuPermission(user.getId());
            List<String> permissionList = menuList.stream()
                    .map(Menu::getPermission)
                    .filter(e -> !StringUtils.isEmpty(e))
                    .distinct()
                    .collect(Collectors.toList());
            return LoginUserDto.builder()
                    .id(user.getId())
                    .avatar(user.getAvatar())
                    .username(user.getUsername())
                    .password(user.getPassword())
                    .nickname(user.getNickname())
                    .email(user.getEmail())
                    .phone(user.getPhone())
                    .status(user.getStatus())
                    .permissionList(permissionList)
                    .build();
        }
        throw new ApiException("找不到用户信息");
    }

    @Override
    public SaTokenInfo login(String username, String password) {
        LoginUserDto loginUserDTO = loadUserByUsername(username);
        String rsaPassword = RsaUtil.decryptByPrivateKey(RsaProperties.privateKey, password);
        if (!BCrypt.checkpw(rsaPassword, loginUserDTO.getPassword())) {
            Asserts.fail("密码不正确");
        }
        LoginHelper.login(loginUserDTO);
        return LoginHelper.getTokenInfo();
    }

    @Override
    public boolean updateLastLoginTime(Long id) {
        UpdateWrapper<User> wrapper = new UpdateWrapper<>();
        wrapper.lambda().set(User::getLastLoginTime, LocalDateTime.now()).eq(User::getId, id);
        return update(wrapper);
    }

    @Override
    public int updatePassword(Long uid, String oldPassword, String newPassword) {
        User user = getById(uid);
        if (ObjectUtil.isNull(user)) {
            return -1;
        }

        if (!BCrypt.checkpw(oldPassword, user.getPassword())) {
            return -2;
        }
        user.setPassword(BCrypt.hashpw(newPassword));
        updateById(user);
        return 1;
    }
}
